<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
    <script async src="https://docs.opencv.org/4.5.0/opencv.js" onload="waitLoadCV()"></script>
</head>
<body>

    <input type="file" id="fileInput" accept="image/*">
    <input type="file" id="stmpl" accept="image/*">
    <button onclick="handleImage()">开始检测</button>https://docs.opencv.org/master/opencv.js
    <canvas id="outputCanvas"></canvas>
    <img id="background2" src="a2-b.jpg" crossorigin="anonymous" hidden>
    <img id="template2" src="a2-s.jpg" crossorigin="anonymous" hidden>
    <script>
        let isOpenCVReady = null
        // 初始化OpenCV
        function waitLoadCV() {
            if (typeof (cv) == "undefined")
                setTimeout(waitLoadCV, 500)
            else {
                cv['onRuntimeInitialized'] = () => {
                    isOpenCVReady = true;
                    console.log("OpenCV 就绪");
                }
                isOpenCVReady = true;
                console.log("OpenCV 就绪2")
            }
        }

        function mrun() {
            let img1 = stmpl.files[0]
            let img2 = stmpl.files[0]
            handleImage(img1, img2)
        }
        // 图像预处理流水线
        function preprocessImage(src) {
            const gray = new cv.Mat();
            cv.cvtColor(src, gray, cv.COLOR_RGBA2GRAY);

            // 边缘检测强化轮廓
            const edges = new cv.Mat();
            cv.Canny(gray, edges, 150, 200); // 调整阈值优化边缘检测

            // 形态学操作增强连续轮廓
            const kernel = cv.getStructuringElement(cv.MORPH_RECT, new cv.Size(3, 3));
            cv.dilate(edges, edges, kernel);

            gray.delete();
            return edges;
        }

        // 执行模板匹配
        function matchTemplate(sceneEdges, templateEdges) {
            const result = new cv.Mat();
            const matchMethod = cv.TM_CCOEFF_NORMED; // 归一化相关系数法

            // 执行边缘图匹配
            cv.matchTemplate(sceneEdges, templateEdges, result, matchMethod);

            // 获取最佳匹配位置
            const minMax = cv.minMaxLoc(result);
            console.log(`最大匹配值: ${minMax.maxVal.toFixed(2)} 位置: (${minMax.maxLoc.x}, ${minMax.maxLoc.y})`);
            const maxLoc = minMax.maxLoc;

            result.delete();
            return maxLoc;
        }

        // 主处理流程
        async function handleImage(img1, img2) {
            const sceneImg = await loadImage(img1);
            const sceneMat = cv.imread(sceneImg);

            const tmplImg = await loadImage(img2);
            let templateMat = cv.imread(tmplImg);
            // 预处理流水线
            const sceneEdges = preprocessImage(sceneMat);
            templateEdges = preprocessImage(templateMat);
            // 执行匹配
            const { x, y } = matchTemplate(sceneEdges, templateEdges);

            // 绘制结果
            const canvas = document.getElementById('outputCanvas');
            cv.imshow(canvas, sceneMat);

            const ctx = canvas.getContext('2d');
            ctx.strokeStyle = '#00FF00';
            ctx.lineWidth = 2;
            ctx.strokeRect(x, y, templateMat.cols, templateMat.rows);

            // 内存清理
            [sceneMat, sceneEdges].forEach(m => m.delete());
        }

        // 图像加载工具函数
        function loadImage(file) {
            return new Promise((resolve) => {
                const reader = new FileReader();
                reader.onload = e => {
                    const img = new Image();
                    img.onload = () => resolve(img);
                    img.src = e.target.result;
                };
                reader.readAsDataURL(file);
            });
        }
    </script>
</body>
</html>